package com.cskaoyan.wordmemorize.common.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cskaoyan.wordmemorize.common.constant.ResultCodeEnum;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.common.exception.ExcelParseException;
import com.cskaoyan.wordmemorize.converter.TopicConverter;
import com.cskaoyan.wordmemorize.dao.entity.ReviewSubTopicDO;
import com.cskaoyan.wordmemorize.dao.entity.ReviewTopicDO;
import com.cskaoyan.wordmemorize.dao.entity.TopicOptionDO;
import com.cskaoyan.wordmemorize.dao.entity.VocDO;
import com.cskaoyan.wordmemorize.dao.mapper.VocMapper;
import com.cskaoyan.wordmemorize.dto.admin.FillTopicExcelDTO;
import com.cskaoyan.wordmemorize.dto.admin.SingleTopicExcelDTO;
import com.cskaoyan.wordmemorize.service.ReviewSubTopicService;
import com.cskaoyan.wordmemorize.service.ReviewTopicOptionService;
import com.cskaoyan.wordmemorize.service.ReviewTopicService;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// 有个很重要的点 DemoDataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class FillTopicExcelListener implements ReadListener<FillTopicExcelDTO> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<FillTopicExcelDTO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 假设这个是一个Mapper，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private ReviewTopicService reviewTopicService;

    private ReviewTopicOptionService reviewTopicOptionService;

    private ReviewSubTopicService reviewSubTopicService;

    private VocMapper vocMapper;


    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param reviewTopicOptionService
     */
    public FillTopicExcelListener(VocMapper vocMapper, ReviewTopicService reviewTopicService, ReviewTopicOptionService reviewTopicOptionService, ReviewSubTopicService reviewSubTopicService) {
        this.vocMapper = vocMapper;
        this.reviewTopicService = reviewTopicService;
        this.reviewTopicOptionService = reviewTopicOptionService;
        this.reviewSubTopicService = reviewSubTopicService;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(FillTopicExcelDTO data, AnalysisContext context) {


        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成！");
    }


    public TopicOptionDO createTopicOption(String item, Integer isAnswer,Integer optionNumber){
        TopicOptionDO topicOptionDO = new TopicOptionDO();
        topicOptionDO.setItem(item);
        topicOptionDO.setIsAnswer(isAnswer);
        topicOptionDO.setOptionNumber(optionNumber);

        return topicOptionDO;

    }

    public ReviewSubTopicDO createSubTopic(Long vocId,String vocabulary,String topicItem,Integer subTopicNumber){
        ReviewSubTopicDO reviewSubTopicDO = new ReviewSubTopicDO();
        reviewSubTopicDO.setVocId(vocId);
        reviewSubTopicDO.setVocabulary(vocabulary);
        reviewSubTopicDO.setTopicItem(topicItem);
        reviewSubTopicDO.setSubTopicNumber(subTopicNumber);

        return reviewSubTopicDO;
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据，开始存储数据库！", cachedDataList.size());
        List<ReviewTopicDO> topicDOList = new ArrayList<>();
        Map<String,List<ReviewSubTopicDO>> subTopicsMap = new HashMap<>();
        Map<String,TopicOptionDO> optionsMap = new HashMap<>();
        cachedDataList.forEach(cachedData -> {
            List<ReviewSubTopicDO> subTopicList = new ArrayList<>();
            StringBuffer topicStringBuffer = new StringBuffer();
            StringBuffer vocabularyStringBuffer = new StringBuffer();

            //  为其他不是, 存进List ，为之后插入做准备
            // 答案1 的存储
            String answer1 = cachedData.getAnswer1();
            QueryWrapper<VocDO> queryWrapper1 = new QueryWrapper<>();
            queryWrapper1.eq("content",answer1);
            VocDO voc1 = vocMapper.selectOne(queryWrapper1);
            String subTopic11 = cachedData.getSubTopic1();
            topicStringBuffer.append(subTopic11 +"\n");
            ReviewSubTopicDO subTopic1 = createSubTopic(voc1.getId(), voc1.getContent(), subTopic11, 1);
            subTopicList.add(subTopic1);

            // 答案2 的存储
            String answer2 = cachedData.getAnswer2();
            QueryWrapper<VocDO> queryWrapper2 = new QueryWrapper<>();
            queryWrapper2.eq("content",answer2);
            VocDO voc2 = vocMapper.selectOne(queryWrapper2);
            String subTopic12 = cachedData.getSubTopic2();
            topicStringBuffer.append(subTopic12 +"\n");
            ReviewSubTopicDO subTopic2 = createSubTopic(voc2.getId(), voc2.getContent(), subTopic12, 2);
            subTopicList.add(subTopic2);

            // 答案3 的存储
            String answer3 = cachedData.getAnswer3();
            QueryWrapper<VocDO> queryWrapper3 = new QueryWrapper<>();
            queryWrapper3.eq("content",answer3);
            VocDO voc3 = vocMapper.selectOne(queryWrapper3);
            String subTopic13 = cachedData.getSubTopic2();
            topicStringBuffer.append(subTopic13 +"\n");
            ReviewSubTopicDO subTopic3 = createSubTopic(voc3.getId(), voc3.getContent(), subTopic13, 3);
            subTopicList.add(subTopic3);

            // 干扰项对象的创建
            TopicOptionDO topicOption = createTopicOption(cachedData.getError(), 0, null);
            optionsMap.put(topicStringBuffer.toString(),topicOption);

            vocabularyStringBuffer.append(cachedData.getSubTopic1() + ",")
                    .append(cachedData.getSubTopic2() + ",")
                    .append(cachedData.getSubTopic3() + ",");
            ReviewTopicDO reviewTopicDO = new ReviewTopicDO();
            reviewTopicDO.setTopicType(2);
            reviewTopicDO.setTopic(topicStringBuffer.toString());
            reviewTopicDO.setVocabulary(vocabularyStringBuffer.toString());
            topicDOList.add(reviewTopicDO);


            subTopicsMap.put(topicStringBuffer.toString(),subTopicList);

        });
        // 题目存储
        reviewTopicService.saveBatch(topicDOList);

        // 答案存储
        topicDOList.forEach(topicDO -> {
            List<ReviewSubTopicDO> subTopicDOS = subTopicsMap.get(topicDO.getTopic());
            subTopicDOS.forEach(subTopicDO -> subTopicDO.setTid(topicDO.getId()));
            // 干扰项的存储
            TopicOptionDO topicOptionDO = optionsMap.get(topicDO.getTopic());
            topicOptionDO.setTid(topicDO.getId());
            reviewTopicOptionService.save(topicOptionDO);
            reviewSubTopicService.saveBatch(subTopicDOS);
        });

        log.info("存储数据库成功！");
    }


    /**
     * 1. 校验单词是否存在
     * 2. 存在 返回false ，不存在 返回ture
     * @param word 被校验的单词
     * @return
     */
    public boolean checkWord(String word) {
        LambdaQueryWrapper<VocDO> vocDOWrapper = new LambdaQueryWrapper<>();
        vocDOWrapper.eq(VocDO::getContent, word);
        VocDO vocDO = vocMapper.selectOne(vocDOWrapper);
        if (vocDO == null) {
            return true;
        }

        return false;
    }
}